#include <iostream>
#include "PerspectiveCamera.h"
#include "Mesh.h"
#include "PointLight.h"


CPerspectiveCamera::CPerspectiveCamera(void)
{
	m_Right = new CVector(CVector::CrossProduct(*m_Target, *m_Up));
	m_Fov = (m_ViewPlane->GetWidth() + m_ViewPlane->GetHeight()) / m_Target->Length();			// Check!
}

CPerspectiveCamera::CPerspectiveCamera(CVector _pos, CVector _tgt):CCamera(_pos, _tgt)
{
	m_Right = new CVector(CVector::CrossProduct(*m_Up, *m_Target));
	m_Fov = (m_ViewPlane->GetWidth() + m_ViewPlane->GetHeight()) / m_Target->Length();			// Check!
}

CPerspectiveCamera::CPerspectiveCamera(CVector _pos, CVector _tgt, CVector _up, float _np, float _fp, float _fov):CCamera(_pos, _tgt, _up, _np, _fp, _fov)
{
	m_Right = new CVector(CVector::CrossProduct(*m_Target, *m_Up));
	m_Fov = (m_ViewPlane->GetWidth() + m_ViewPlane->GetHeight()) / m_Target->Length();			// Check!
}

CPerspectiveCamera::~CPerspectiveCamera(void)
{
	delete m_Position;
	delete m_Target;
	delete m_Up;
	delete m_Right;
	delete m_ViewPlane;
}

CLightIntensity *DetermineBgColor(int _x, int _y, int _width, int _height)
{
	int _dX = _width / 6;
	int _dY = _height / 6;

	if (_x < _dX)
	{
		if (_y < _dY)
		{
			return new CLightIntensity(1.0f, 0.0f, 0.0f);
		}
		else if (_y < _dY * 2)
		{
			return new CLightIntensity(0.8f, 0.0f, 0.0f);
		}
		else if (_y < _dY * 3)
		{
			return new CLightIntensity(0.6f, 0.0f, 0.0f);			
		}
		else if (_y < _dY * 4)
		{
			return new CLightIntensity(0.4f, 0.0f, 0.0f);
		}
		else if (_y < _dY * 5)
		{
			return new CLightIntensity(0.1f, 0.0f, 0.0f);
		}
		else
		{
			return new CLightIntensity();
			
		}
	}
	else if (_x < _dX * 2)
	{
		if (_y < _dY)
		{
			return new CLightIntensity(0.0f, 1.0f, 0.0f);
		}
		else if (_y < _dY * 2)
		{
			return new CLightIntensity(0.0f, 0.8f, 0.0f);
		}
		else if (_y < _dY * 3)
		{
			return new CLightIntensity(0.0f, 0.6f, 0.0f);
		}
		else if (_y < _dY * 4)
		{
			
			return new CLightIntensity(0.0f, 0.4f, 0.0f);
		}
		else if (_y < _dY * 5)
		{
			
			return new CLightIntensity(0.0f, 0.2f, 0.0f);
		}
		else
		{
			return new CLightIntensity(0.0f, 0.1f, 0.0f);
			
		}
	}
	else if (_x < _dX * 3)
	{
		if (_y < _dY)
		{
			return new CLightIntensity(0.0f, 0.0f, 1.0f);
		}
		else if (_y < _dY * 2)
		{
			return new CLightIntensity(0.0f, 0.0f, 0.8f);
		}
		else if (_y < _dY * 3)
		{
			
			return new CLightIntensity(0.0f, 0.0f, 0.6f);
		}
		else if (_y < _dY * 4)
		{
			return new CLightIntensity(0.0f, 0.0f, 0.4f);
		}
		else if (_y < _dY * 5)
		{
			return new CLightIntensity(0.0f, 0.0f, 0.2f);
			
		}
		else
		{
			return new CLightIntensity(0.0f, 0.0f, 0.1f);
			
		}
	}
	else if (_x < _dX * 4)
	{
		if (_y < _dY)
		{
			return new CLightIntensity(0.7f, 0.0f, 0.7f);
		}
		else if (_y < _dY * 2)
		{
			return new CLightIntensity(1.0f, 0.0f, 1.0f);
		}
		else if (_y < _dY * 3)
		{
			return new CLightIntensity(1.0f, 0.0f, 0.6f);
		}
		else if (_y < _dY * 4)
		{
			
			return new CLightIntensity(1.0f, 0.0f, 0.4f);
		}
		else if (_y < _dY * 5)
		{
			
			return new CLightIntensity(1.0f, 0.0f, 0.2f);
		}
		else
		{
			
			return new CLightIntensity(1.0f, 0.0f, 0.0f);
		}
	}
	else if (_x < _dX * 5)
	{
		if (_y < _dY)
		{
			return new CLightIntensity(0.0f, 0.6f, 0.7f);
		}
		else if (_y < _dY * 2)
		{
			return new CLightIntensity(0.0f, 0.9f, 0.7f);
		}
		else if (_y < _dY * 3)
		{
			return new CLightIntensity(0.0f, 1.0f, 0.6f);
		}
		else if (_y < _dY * 4)
		{
			
			return new CLightIntensity(0.0f, 1.0f, 0.4f);
		}
		else if (_y < _dY * 5)
		{
			
			return new CLightIntensity(0.0f, 1.0f, 0.2f);
		}
		else
		{
			
			return new CLightIntensity(0.0f, 1.0f, 0.0f);
		}
	}
	else
	{
		if (_y < _dY)
		{
			return new CLightIntensity(1.0f, 1.0f, 1.0f);
		}
		else if (_y < _dY * 2)
		{
			return new CLightIntensity(1.0f, 1.0f, 0.8f);
		}
		else if (_y < _dY * 3)
		{
			return new CLightIntensity(1.0f, 1.0f, 0.6f);
		}
		else if (_y < _dY * 4)
		{
			
			return new CLightIntensity(1.0f, 1.0f, 0.4f);
		}
		else if (_y < _dY * 5)
		{
			
			return new CLightIntensity(1.0f, 1.0f, 0.2f);
		}
		else
		{
			
			return new CLightIntensity(1.0f, 1.0f, 0.0f);
		}
	}
}

void CPerspectiveCamera::Render()
{
	//RenderInstruction2();
	RenderInstruction3();
	//RenderInstruction4();
	//Test();
}

CRay* CPerspectiveCamera::ComputeCameraRayAtPosition(int _x, int _y)
{
	float normalX = (_x * 2.0f / (float) m_ViewPlane->GetWidth()) - 1.0f;
	float normalY = (_y * 2.0f / (float) m_ViewPlane->GetHeight()) - 1.0f;
	CVector imgPoint = (*m_Right) * normalX + (*m_Up) * normalY + (*m_Position) + (*m_Target);
	CVector rayDir = imgPoint - (*m_Position);

	return new CRay((*m_Position), rayDir);
}

void CPerspectiveCamera::RenderInstruction2()
{
	CRay *actRay = 0;
	CLightIntensity *bgColor = new CLightIntensity();
	CLightIntensity *sph1Color = new CLightIntensity(0.2f, 0.2f, 1.0f);
	CLightIntensity *sph2Color = new CLightIntensity(1.0f, 0.2f, 0.2f);
	CSphere *sphere1 = new CSphere(CVector(0.0f, 0.0f, 0.0f), 1.7f);
	CSphere *sphere2 = new CSphere(CVector(2.4f, 0.0f, 1.0f), 1.2f);
	int interResult = -1;

	for (int i = 0; i < m_ViewPlane->GetWidth(); i++)
	{
		for (int j = 0; j < m_ViewPlane->GetHeight(); j++)
		{
			actRay = ComputeCameraRayAtPosition(i, j);
	//		interResult = sphere2->Intersect(*actRay, CVector(), CVector());

			bgColor = DetermineBgColor(i, j, m_ViewPlane->GetWidth(), m_ViewPlane->GetHeight());

			if (interResult > 0)
			{
//				interResult = sphere1->Intersect(*actRay, CVector(), CVector());

				if (interResult > 0)
					m_ViewPlane->SetPixel(i, j, *sph1Color);
				else
					m_ViewPlane->SetPixel(i, j, *sph2Color);
			}
			else
			{
//				interResult = sphere1->Intersect(*actRay, CVector(), CVector());
				
				if (interResult > 0)
					m_ViewPlane->SetPixel(i, j, *sph1Color);
				else
					m_ViewPlane->SetPixel(i, j, *bgColor);
			}
		}
	}

	MASGK::CTgaBuffer *buf = new MASGK::CTgaBuffer(m_ViewPlane->GetWidth(), m_ViewPlane->GetHeight());
	buf->Save (m_ViewPlane->GetWidth(), m_ViewPlane->GetHeight(), *m_ViewPlane, "FGK_2_P.tga");
	delete buf;
}

void CPerspectiveCamera::RenderInstruction3()
{
	CRay *actRay = 0;
	int interResult = -1;
	CLightIntensity *bgColor = new CLightIntensity();
	IsectData isectData;
	int percent = 0;
	int prevPercent = 0;

	std::cout << "Rendering..." << std::endl;

	for (int i = 0; i < m_ViewPlane->GetWidth(); i++)
	{
		for (int j = 0; j < m_ViewPlane->GetHeight(); j++)
		{
			actRay = ComputeCameraRayAtPosition(i, j);

			int percent = ((int) (((i * j) * 100.0f) / ((m_ViewPlane->GetWidth() * m_ViewPlane->GetHeight()) * 1.0f)));

			if (percent != prevPercent && percent != 0 && percent % 10 == 0)
			{
				if (percent > prevPercent)
				{
					prevPercent = percent;
					std::cout << (int) (((i * j) * 100.0f) / ((m_ViewPlane->GetWidth() * m_ViewPlane->GetHeight()) * 1.0f)) << "%" << std::endl;
				}
			}

			for (std::vector<CPrimitive*>::iterator object = m_Scene->m_Objects.begin(); object != m_Scene->m_Objects.end(); object++)
			{
				interResult = (*object)->Intersect(*actRay, isectData);

				if (interResult > 0)
				{
					m_ViewPlane->SetPixel(i, j, CLightIntensity(*(*object)->GetColor()));
				}
			}
		}

	}

	MASGK::CTgaBuffer *buf = new MASGK::CTgaBuffer(m_ViewPlane->GetWidth(), m_ViewPlane->GetHeight());
	buf->Save (m_ViewPlane->GetWidth(), m_ViewPlane->GetHeight(), *m_ViewPlane, "FGK_3_P.tga");
	delete buf;
}

void CPerspectiveCamera::RenderInstruction4()
{
	CRay *actRay = NULL;
	CRay *shadowRay = NULL;
	CVector p;
	int interResult = -1;
	CLightIntensity *bgColor = new CLightIntensity();
	IsectData isectData;
	bool inShadow = false;

	for (int i = 0; i < m_ViewPlane->GetWidth(); i++)
	{
		for (int j = 0; j < m_ViewPlane->GetHeight(); j++)
		{
			actRay = ComputeCameraRayAtPosition(i, j);

			for (std::vector<CPrimitive*>::iterator object = m_Scene->m_Objects.begin(); object != m_Scene->m_Objects.end(); object++)
			{
				if ((*object)->m_IsMesh)
				{
					CMesh *currentMesh = (CMesh*) (*object);
					interResult = currentMesh->Intersect(*actRay, isectData, p);
				}
				else
				{
					interResult = (*object)->Intersect(*actRay, isectData);
					CTriangle *triangle = (CTriangle*) (*object);
					p =     (*triangle->GetA() * isectData.u) + 
							(*triangle->GetB() * isectData.v) +
							(*triangle->GetC() * isectData.t);				
				}

				if (interResult > 0)
				{
					// Trace ray to light
					// for(...) each light in scene			TODO!
					CPointLight *light = (CPointLight*) m_Scene->m_Lights[0];		// TEMP!!!
					CVector lightPos = *light->GetPosition();						// TEMP!!!
					p.SetZ(p.GetZ() + m_Position->GetZ());							// CHECK!!! To put origin of shadow ray on intersected triangle
					shadowRay = new CRay(p, CVector(lightPos - p));

					if (p.GetZ() < 0.01f)
						int breaka = -1;

					for (std::vector<CPrimitive*>::iterator objectBetweenLight = m_Scene->m_Objects.begin(); objectBetweenLight != m_Scene->m_Objects.end(); objectBetweenLight++)
					{
						if ((*objectBetweenLight)->m_IsMesh)
						{
							CMesh *currentMesh = (CMesh*) (*objectBetweenLight);
							interResult = currentMesh->Intersect(*shadowRay, isectData, p);
						}
						else
							interResult = (*objectBetweenLight)->Intersect(*shadowRay, isectData);

						if (interResult > 0)		// There is object between light and P, so there will be a shadow (black color)
						{
							m_ViewPlane->SetPixel(i, j, CLightIntensity());
							inShadow = true;
							break;
						}
						else
							inShadow = false;
					}

					if (!inShadow)
						m_ViewPlane->SetPixel(i, j, CLightIntensity(*(*object)->GetColor()));
				}
			}
		}

	}

	MASGK::CTgaBuffer *buf = new MASGK::CTgaBuffer(m_ViewPlane->GetWidth(), m_ViewPlane->GetHeight());
	buf->Save (m_ViewPlane->GetWidth(), m_ViewPlane->GetHeight(), *m_ViewPlane, "FGK_4_P.tga");
	delete buf;
}

void CPerspectiveCamera::Test()
{
	CRay *actRay = 0;
	int interResult = -1;
	CLightIntensity *bgColor = new CLightIntensity();
	IsectData isectData;

	CTriangle *triangle = new CTriangle(  
							CVector(-1.0f, 0.9f, 0.0f),
							CVector(-0.9f, 0.9f, 0.0f),
							CVector(-0.95f, 1.0f, 0.0f),
							CLightIntensity(1.0f, 0.0f, 0.0f)
							);

	CTriangle *triangle2 = new CTriangle(  
							CVector(0.9f, 0.9f, 0.0f),
							CVector(1.0f, 0.9f, 0.0f),
							CVector(0.95f, 1.0f, 0.0f),
							CLightIntensity(0.0f, 1.0f, 0.0f)
							);

	CTriangle *triangle3 = new CTriangle(  
							CVector(0.9f, -1.0f, 0.0f),
							CVector(1.0f, -1.0f, 0.0f),
							CVector(0.95f, -0.9f, 0.0f),
							CLightIntensity(0.0f, 0.0f, 1.0f)
							);

	CTriangle *triangle4 = new CTriangle(  
							CVector(-1.0f, -1.0f, 0.0f),
							CVector(-0.9f, -1.0f, 0.0f),
							CVector(-0.95f, -0.9f, 0.0f),
							CLightIntensity(1.0f, 1.0f, 0.0f)
							);

	CTriangle *triangle5 = new CTriangle(  
							CVector(-0.05f, 0.0f, 0.0f),
							CVector(0.05f, 0.0f, 0.0f),
							CVector(0.0f, 0.1f, 0.0f),
							CLightIntensity(1.0f, 1.0f, 1.0f)
							);

/*		CTriangle *triangleFace1 = new CTriangle(  
							CVector(-0.9f, 0.6f, 1.0f),
							CVector(0.0f, 0.0f, 1.0f),
							CVector(-0.6f, 0.9f, 1.0f),
							
							CLightIntensity(1.0f, 1.0f, 1.0f)
							);

		CTriangle *triangleFace2 = new CTriangle(  
							CVector(-0.6f, 0.9f, 1.0f),
							CVector(0.0f, 0.0f, 1.0f),
							CVector(0.0f, 1.0f, 1.0f),
							
							CLightIntensity(0.7f, 0.7f, 0.7f)
							);

		CTriangle *triangleFace7 = new CTriangle(  
							CVector(0.0f, 1.0f, 1.0f),
							CVector(0.0f, 0.0f, 1.0f),
							CVector(0.6f, 0.9f, 1.0f),
							
							CLightIntensity(1.0f, 1.0f, 0.0f)
							);

		CTriangle *triangleFace8 = new CTriangle(  
							CVector(0.6f, 0.9f, 2.0f),
							CVector(0.0f, 0.0f, 1.0f),
							CVector(0.9f, 0.6f, 2.0f),
							
							CLightIntensity(0.7f, 0.7f, 0.0f)
							);

	*/

		CTriangle *triangleFace1 = new CTriangle(  
							CVector(0.0f, 0.0f, 0.0f),
							CVector(1.0f, 1.0f, 0.0f),
							CVector(1.0f, 0.0f, 0.0f),

							CLightIntensity(1.0f, 1.0f, 1.0f)
							);

		CTriangle *triangleFace2 = new CTriangle(  	
							CVector(0.0f, 1.0f, 0.0f),
							CVector(1.0f, 1.0f, 0.0f),
							CVector(0.0f, 0.0f, 0.0f),
							
							CLightIntensity(0.7f, 0.7f, 0.7f)
							);

		CTriangle *triangleFace7 = new CTriangle(  	
							CVector(1.0f, 0.0f, 0.0f),	
							CVector(1.0f, 0.0f, 1.0f),
							CVector(1.0f, 1.0f, 1.0f),

							CLightIntensity(1.0f, 1.0f, 0.0f)
							);

		CTriangle *triangleFace8 = new CTriangle(  
							CVector(1.0f, 0.0f, 0.0f),
							CVector(1.0f, 1.0f, 1.0f),
							CVector(1.0f, 1.0f, 0.0f),

							CLightIntensity(0.7f, 0.7f, 0.0f)
							);

		/*


		*/

	for (int i = 0; i < m_ViewPlane->GetWidth(); i++)
	{
		for (int j = 0; j < m_ViewPlane->GetHeight(); j++)
		{
			actRay = ComputeCameraRayAtPosition(i, j);

			interResult = triangle->Intersect(*actRay, isectData);

			if (interResult > 0)
			{
		//		m_ViewPlane->SetPixel(i, j, *triangle->GetColor());
			}

			interResult = triangle2->Intersect(*actRay, isectData);

			if (interResult > 0)
			{
		//		m_ViewPlane->SetPixel(i, j, *triangle2->GetColor());
			}

			interResult = triangle3->Intersect(*actRay, isectData);

			if (interResult > 0)
			{
		//		m_ViewPlane->SetPixel(i, j, *triangle3->GetColor());
			}

			interResult = triangle4->Intersect(*actRay, isectData);

			if (interResult > 0)
			{
		//		m_ViewPlane->SetPixel(i, j, *triangle4->GetColor());
			}
/*
			interResult = triangle5->Intersect(*actRay, CVector(), CVector());

			if (interResult > 0)
			{
				m_ViewPlane->SetPixel(i, j, *triangle5->GetColor());
			}
*/
			interResult = triangleFace7->Intersect(*actRay, isectData);

			if (interResult > 0)
			{
				m_ViewPlane->SetPixel(i, j, *triangleFace7->GetColor());
			}

			interResult = triangleFace8->Intersect(*actRay, isectData);

			if (interResult > 0)
			{
				m_ViewPlane->SetPixel(i, j, *triangleFace8->GetColor());
			}

			interResult = triangleFace1->Intersect(*actRay, isectData);

			if (interResult > 0)
			{
				m_ViewPlane->SetPixel(i, j, *triangleFace1->GetColor());
			}

			interResult = triangleFace2->Intersect(*actRay, isectData);

			if (interResult > 0)
			{
				m_ViewPlane->SetPixel(i, j, *triangleFace2->GetColor());
			}

		}
	}

	MASGK::CTgaBuffer *buf = new MASGK::CTgaBuffer(m_ViewPlane->GetWidth(), m_ViewPlane->GetHeight());
	buf->Save (m_ViewPlane->GetWidth(), m_ViewPlane->GetHeight(), *m_ViewPlane, "TEST_P.tga");
	delete buf;
}